//
// Created by xbm on 23-3-2.
//

#ifndef WEBSERVER_BUFFER_H
#define WEBSERVER_BUFFER_H
#include <string>
#include <vector>
#include <utility>
#include <mutex>
#include <cassert>
#include <condition_variable>
#include <chrono>
#include <iostream>
#include "nonCopyable.h"

using namespace std;
namespace logging::buffer {
template<int SIZE>
class FixedBuffer : util::NonCopyable {
public:
    FixedBuffer() {data_.reserve(SIZE);}

    void append(string_view buf) {
        assert(size() + buf.size() <= SIZE);
        data_ += buf;
    }

    int size() { return data_.size(); }

    int available() { return SIZE - size(); }

    string&getData(){return data_;}

    void reset() { data_.resize(0); }

private:
    string data_;
};

constexpr int SmallBuffer = 4000;
constexpr int LargeBuffer = 4000 * 1000;
constexpr int BufferSize = SmallBuffer;
using Buffer = FixedBuffer<BufferSize>;

template<int SIZE>
class FixedBufferPool {
public:
    void write(string_view sv);
    void waitForBuffer(int time=1000);
    bool hasUsedBuffer();
    Buffer& getUsedBuffer();
    void moveUsed(){pos_used= getNext(pos_used);};

private:
    int getNext(int cur){return (cur+1)%SIZE;}
    bool checkBufferFull();
    bool checkBufferEmpty();
    void moveBuffer();

    mutex write_mx_;
    condition_variable has_used_buffer_;

    vector <Buffer> buffers_=vector <Buffer> (SIZE);
    int pos_using = 0;
    int pos_used = 0;
};

template<int SIZE>
Buffer &FixedBufferPool<SIZE>::getUsedBuffer() {
    return buffers_[pos_used];
}

template<int SIZE>
bool FixedBufferPool<SIZE>::hasUsedBuffer() {
    return pos_using!=pos_used;
}

template<int SIZE>
void FixedBufferPool<SIZE>::waitForBuffer(int time) {
    static mutex mx;
    unique_lock ul(mx);
    if(hasUsedBuffer()) return;
    if(has_used_buffer_.wait_for(ul,chrono::milliseconds(time))==cv_status::timeout){
        unique_lock write_ul(write_mx_,try_to_lock);
        if(write_ul.owns_lock()){
            if(hasUsedBuffer()) return;
            if(!checkBufferEmpty()){
                moveBuffer();
            }
        }
    }
}

template<int SIZE>
bool FixedBufferPool<SIZE>::checkBufferEmpty() {
    return buffers_[pos_using].size()==0;
}

template<int SIZE>
bool FixedBufferPool<SIZE>::checkBufferFull() {
    return buffers_[pos_using].available()==0;
}

template<int SIZE>
void FixedBufferPool<SIZE>::moveBuffer() {
    int next= getNext(pos_using);
    while (next==pos_used){
        this_thread::yield();
        next= getNext(pos_using);
    }

    pos_using=next;
    has_used_buffer_.notify_one();

}


template<int SIZE>
void FixedBufferPool<SIZE>::write(string_view sv) {
    lock_guard lg(write_mx_);
    do {
        int sz=sv.size();
        int avail=min(sz,buffers_[pos_using].available());

        buffers_[pos_using].append(sv.substr(0,avail));
        if(checkBufferFull()){
            moveBuffer();
        }
        sv=sv.substr(avail);

    }while(!sv.empty());
}


constexpr int BufferPoolSize = 16;
using BufferPool = FixedBufferPool<BufferPoolSize>;

}




#endif //WEBSERVER_BUFFER_H
